package dlock

import (
	"context"
	"errors"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/google/uuid"
)

// 错误定义
var (
	ErrLockAcquireFailed = errors.New("获取锁失败")
	ErrLockReleaseFailed = errors.New("释放锁失败")
)

// 分布式锁接口
type DistributedLock interface {
	// Lock 获取锁
	Lock(ctx context.Context, key string, ttl time.Duration) (string, error)
	// Unlock 释放锁
	Unlock(ctx context.Context, key string, value string) error
	// TryLock 尝试获取锁，立即返回结果
	TryLock(ctx context.Context, key string, ttl time.Duration) (string, error)
}

// RedisLock Redis实现的分布式锁
type RedisLock struct {
	client *redis.Client
	prefix string
}

// 释放锁的Lua脚本，确保原子性操作
const releaseLockScript = `
if redis.call('GET', KEYS[1]) == ARGV[1] then
	return redis.call('DEL', KEYS[1])
else
	return 0
end
`

// NewRedisLock 创建Redis分布式锁
func NewRedisLock(client *redis.Client, prefix string) *RedisLock {
	if prefix == "" {
		prefix = "dlock:"
	}
	return &RedisLock{
		client: client,
		prefix: prefix,
	}
}

// Lock 获取锁，如果获取不到则重试，直到超时
func (rl *RedisLock) Lock(ctx context.Context, key string, ttl time.Duration) (string, error) {
	lockKey := rl.prefix + key
	lockValue := uuid.New().String()

	retryInterval := 100 * time.Millisecond
	timeout := 5 * time.Second

	timer := time.NewTimer(timeout)
	defer timer.Stop()

	for {
		success, err := rl.client.SetNX(ctx, lockKey, lockValue, ttl).Result()
		if err != nil {
			return "", err
		}
		if success {
			return lockValue, nil
		}

		// 等待一段时间后重试
		select {
		case <-ctx.Done():
			return "", ctx.Err()
		case <-timer.C:
			return "", ErrLockAcquireFailed
		case <-time.After(retryInterval):
			// 继续重试
		}
	}
}

// TryLock 尝试获取锁，立即返回结果
func (rl *RedisLock) TryLock(ctx context.Context, key string, ttl time.Duration) (string, error) {
	lockKey := rl.prefix + key
	lockValue := uuid.New().String()

	success, err := rl.client.SetNX(ctx, lockKey, lockValue, ttl).Result()
	if err != nil {
		return "", err
	}
	if !success {
		return "", ErrLockAcquireFailed
	}

	return lockValue, nil
}

// Unlock 释放锁
func (rl *RedisLock) Unlock(ctx context.Context, key string, value string) error {
	lockKey := rl.prefix + key

	// 使用Lua脚本确保原子性释放锁
	result, err := rl.client.Eval(ctx, releaseLockScript, []string{lockKey}, value).Result()
	if err != nil {
		return err
	}

	if result.(int64) != 1 {
		return ErrLockReleaseFailed
	}

	return nil
}

// AutoRefreshLock 自动刷新锁的有效期
type AutoRefreshLock struct {
	redisLock *RedisLock
	key       string
	value     string
	ttl       time.Duration
	stopCh    chan struct{}
}

// NewAutoRefreshLock 创建一个自动刷新的锁
func NewAutoRefreshLock(redisLock *RedisLock, key string, value string, ttl time.Duration) *AutoRefreshLock {
	return &AutoRefreshLock{
		redisLock: redisLock,
		key:       key,
		value:     value,
		ttl:       ttl,
		stopCh:    make(chan struct{}),
	}
}

// StartAutoRefresh 开始自动刷新
func (arl *AutoRefreshLock) StartAutoRefresh(ctx context.Context) {
	refreshInterval := arl.ttl / 3
	ticker := time.NewTicker(refreshInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			// 刷新锁的过期时间
			lockKey := arl.redisLock.prefix + arl.key
			// 先检查锁是否还是自己的
			val, err := arl.redisLock.client.Get(ctx, lockKey).Result()
			if err != nil || val != arl.value {
				// 锁已经不属于自己了，停止刷新
				return
			}
			// 刷新过期时间
			arl.redisLock.client.Expire(ctx, lockKey, arl.ttl)
		case <-arl.stopCh:
			return
		case <-ctx.Done():
			return
		}
	}
}

// StopAutoRefresh 停止自动刷新
func (arl *AutoRefreshLock) StopAutoRefresh() {
	close(arl.stopCh)
}
